const std = @import("std");
const rl = @import("raylib");
const zlua = @import("zlua");
// const flecs = @import("zflecs");
const components = @import("./components.zig");
const System = @import("./system.zig");
const systems = @import("./systems.zig");
const prefabs = @import("./prefabs.zig");
const ecs = @import("ecs");
const z = @import("zgui");
const c = @cImport({
    @cDefine("NO_FONT_AWESOME", "1");
    @cInclude("rlImGui.h");
});
const Lua = zlua.Lua;
const Allocator = std.mem.Allocator;
const print = std.debug.print;
pub const Config = struct {
    fps: u16 = 60,
    fixed_fps: u16 = 60,
    allocator: std.mem.Allocator = std.heap.smp_allocator,
    reg: *ecs.Registry,
};
pub const SystemArrayList = std.ArrayList(System);
pub const Game = struct {
    const Self = @This();

    var debug_allocator = std.heap.DebugAllocator(.{}).init;
    reg: *ecs.Registry,
    arena_allocator: std.heap.ArenaAllocator,
    draw_systems: SystemArrayList,
    update_systems: SystemArrayList,
    allocator: std.mem.Allocator,

    pub fn init(config: Config) Self {
        var self: Self = .{
            .allocator = config.allocator,
            .arena_allocator = std.heap.ArenaAllocator.init(config.allocator),
            .reg = config.reg,
            .draw_systems = SystemArrayList.init(config.allocator),
            .update_systems = SystemArrayList.init(config.allocator),
        };
        var singletons = self.reg.singletons();
        singletons.add(components.GameConfig{
            .fixed_fps = config.fixed_fps,
            .fps = config.fps,
        });
        singletons.add(components.GameState{
            .duration_per_frame = 1 / @as(f32, @floatFromInt(config.fixed_fps)),
        });
        var vm = Lua.init(config.allocator) catch unreachable;
        vm.openLibs();
        singletons.add(components.LuaVm{
            .vm = vm,
        });
        // add examples entity
        const ent = self.reg.create();
        prefabs.create_actor(self.reg, ent);
        // add systems
        var ds = self.allocator.create(systems.DrawSystem) catch unreachable;
        ds.* = systems.DrawSystem.init(self.reg);
        self.draw_systems.append(ds.system()) catch unreachable;

        return self;
    }

    pub fn update(self: *Self) void {
        const dt = rl.getFrameTime();
        const t = rl.getTime();
        _ = t;
        var singletons = self.reg.singletons();
        var game_state = singletons.get(components.GameState);
        // const game_config = singletons.get(components.GameConfig);
        game_state.acc_duration += dt;
        if (game_state.acc_duration > game_state.duration_per_frame) {
            // TODO: 低fps时，达不到fps
            // self.systems.fixed_update(game_state.acc_duration, t);
            game_state.acc_duration = 0;
        }
        rl.beginDrawing();
        rl.clearBackground(rl.Color.dark_gray);
        rl.drawFPS(10, 10);
        // self.systems.draw(dt, t);
        for (self.draw_systems.items) |system| {
            system.run();
        }
        rl.endDrawing();
        _ = self.arena_allocator.reset(.{ .retain_with_limit = 250 * 1024 * 1024 });
    }

    pub fn deinit(self: *Self) void {
        var singletons = self.reg.singletons();
        var lua_vm = singletons.get(components.LuaVm);
        lua_vm.vm.deinit();
        self.arena_allocator.deinit();
        // de
        // self.allocator.destroy(self);
    }
    pub fn init_window(self: *Self) void {
        var singletons = self.reg.singletons();
        const game_config = singletons.get(components.GameConfig);
        rl.initWindow(800, 600, "zig-raylib-gamedev");
        if (!rl.isWindowReady()) {
            unreachable;
        }
        rl.setTargetFPS(@intCast(game_config.fps));
        if (rl.getMonitorCount() > 1) {
            rl.setWindowMonitor(1);
        }

        c.rlImGuiSetup(true);
        z.initNoContext(self.allocator);
        // systems.setup(self);
    }
    pub fn close_window(_: *Self) void {
        rl.closeWindow();
        c.rlImGuiShutdown();
        z.deinitNoContext();
    }
    pub fn should_close_window(_: *Self) bool {
        return rl.windowShouldClose();
    }
};
